<?php
/*****************************************************************
	Std PHP Framework
	Module CurlMulti Bot Core
	$Id: mod_core.php 38 2008-05-11 17:41:36Z RusAnon $
*****************************************************************/
$useragents = array(
	"ie"	=> "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)",
	"ie7"	=> "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
	"ff"	=> "Mozilla/5.0 (Windows; U; Windows NT 5.1; nl-NL; rv:1.7.5) Gecko/20041202 Firefox/1.0",
	"ff1.5"	=> "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7",
	"ff2"	=> "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6",
	"safari"=> "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/521.25 (KHTML, like Gecko) Safari/521.24",
	"opera" => "Opera/9.10 (Windows NT 5.1; U; en)",
);

$argl = array_merge($argl,array(
	"proxy-type" => array(
		"required"	=> 0,
		"hasvalue"	=> 1,
		"default"	=> "none",
		"desc"		=> "Proxy type used. Supported : none,tor (127.0.0.1:9050),socks5,http",
	),
	"proxy-max-errors" => array(
		"required"	=> 0,
		"hasvalue"	=> 1,
		"default"	=> 10,
		"desc"		=> "Proxy error limit. 0 means no limit",
	),	
	"proxy" => array(
		"required"	=> 0,
		"hasvalue"	=> 1,
		"default"	=> "",
		"desc"		=> "Proxy or file with proxies list if proxy-type is socks5 or http",
	),
	"timeout" => array(
		"required"	=> 0,
		"hasvalue"	=> 1,
		"default"	=> 15,
		"desc"		=> "Timeout for cURL HTTP request.",
	),
	"debug" => array(
		"required"	=> 0,
		"hasvalue"	=> 1,
		"default"	=> 0,
		"desc"		=> "Debug level.",
	),	
	"user-agent" => array(
		"required"	=> 0,
		"hasvalue"	=> 1,
		"default"	=> "ie",
		"desc"		=> "User-agent provided by bot. Can be : ie,ie7,ff,ff1.5,ff2,safari,opera",
	),
));
function GetIntFromIP($ip)
{
	preg_match("~(\d+)\.(\d+)\.(\d+)\.(\d+)~",$ip,$bits);
	return ($bits[1] << 24) | ($bits[2] << 16) | ($bits[3] << 8) | ($bits[4]);
}
function GetIPFromInt($int)
{
	return ((($int & 0xFF000000) >> 24)& 0xFF).".".(($int & 0xFF0000) >> 16).".".(($int & 0xFF00) >> 8).".".($int & 0xFF);
}
function DoHTTPRequestPM($p)
{
	global $MultiCurlHandle,$ThreadID,$MultiCurlActive,$MultiCurlHandles,$MultiCurlHandlesR,$MultiCurlHandlesIndex,$MultiCurlArray;
	$defaults = array(
	'url'=>'',
	'cookies'=>'',
	'ref'=>$p['url'],
	'data'=>"",
	'proxytype'=>"none",
	'proxy'=>"",
	'ua'=>"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.5) Gecko/20070713 Firefox/2.0.0.5",
	'timeout'=>5,
	'debug'=>0,
	'TID'=>$ThreadID,
	'function'=>'',
	'params'=>array(),
	);
	if (!isset($p['function']))
	{
		$dbbt = debug_backtrace();
		$defaults['function']=$dbbt[1]['function'];
	}
	foreach ($defaults as $k => $v)
	{
		if (!isset($p[$k]))
			$p[$k]=$v;
	}
	
	if (!$MultiCurlHandle)
		$MultiCurlHandle = curl_multi_init();
	$MultiCurlHandlesIndex++;
        $MultiCurlHandles[$MultiCurlHandlesIndex] = curl_init();
        $ch =&$MultiCurlHandles[$MultiCurlHandlesIndex];
        $MultiCurlHandlesR[$ch]=$MultiCurlHandlesIndex;
        
        $MultiCurlArray[$MultiCurlHandlesIndex]=array("TID"=>$p['TID'],'function'=>$p['function'],'params'=>$p['params']);
        
        curl_setopt($ch,CURLOPT_FORBID_REUSE,true);
        curl_setopt($ch,CURLOPT_HEADER,true);
        curl_setopt($ch,CURLOPT_TIMEOUT,$p['timeout']);
        curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
        curl_setopt($ch,CURLOPT_URL,$p['url']);
        curl_setopt($ch,CURLOPT_COOKIE,$p['cookies']);
        curl_setopt($ch,CURLOPT_ENCODING,"");
        curl_setopt($ch,CURLOPT_REFERER,$p['ref']);
        curl_setopt($ch,CURLOPT_USERAGENT,$p['ua']);
        if ($p['data'])
        {
        	$method = "POST";
                curl_setopt($ch,CURLOPT_POST,true);
                curl_setopt($ch,CURLOPT_POSTFIELDS,$p['data']);
        }
        else
        {
        	$method = "GET";
                curl_setopt($ch,CURLOPT_POST,false);
                curl_setopt($ch,CURLOPT_HTTPGET,true);
        }
        if ($p['proxytype'] == "tor")
        {
		curl_setopt($ch,CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
		curl_setopt($ch,CURLOPT_PROXY,"127.0.0.1:9050");
	}
	else if ($p['proxytype'] == "socks5")
	{
		curl_setopt($ch,CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
		curl_setopt($ch,CURLOPT_PROXY,$p['proxy']);
	}
	else if ($p['proxytype'] == "http")
	{
		curl_setopt($ch,CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
		curl_setopt($ch,CURLOPT_PROXY,$p['proxy']);
	}
	if ($p['debug'] == 1)
	{
		EchoMsg("$method $p[url]");
	}
	else if ($p['debug'] == 2 OR $p['debug'] == 3)
	{
		EchoMsg("$method $p[url] ; Content : $p[data] ; Cookies : $p[cookies] ; Referer : $p[ref] ; Proxy $p[proxytype]:$p[proxy] ;");
	}
	curl_multi_add_handle($MultiCurlHandle,$MultiCurlHandles[$MultiCurlHandlesIndex]);
	if (!$MultiCurlActive)
	{
		$MultiCurlActive = 1;
		AddThread("MultiCUrl",'CurlMultipleWait');
	}
	return $MultiCurlHandlesIndex;
}
function CurlMultipleWait($MultiCurlThreadID,$nothing)
{
	global $MultiCurlHandle,$MultiCurlActive,$MultiCurlHandles,$MultiCurlHandlesR,$MultiCurlHandlesIndex,$MultiCurlArray;
	EchoMsgDebug("CurlMultipleWait()",5);
	$mrc = curl_multi_exec($MultiCurlHandle,$active);
	EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." curl_multi_exec()=$mrc , active = $active",6);
	if ($active and ($mrc == CURLM_OK OR $mrc == CURLM_CALL_MULTI_PERFORM)) 
	{
		EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." before curl_multi_select()",7);
		if (curl_multi_select($MultiCurlHandle) != -1) 
		{
			EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." curl_multi_select() succeeded",6);
			do 
			{
				EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." before curl_multi_exec()",7);
				$mrc = curl_multi_exec($MultiCurlHandle, $active);
				EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." curl_multi_exec()=$mrc , active=$active",6);
			} while ($mrc == CURLM_CALL_MULTI_PERFORM);
		}
		else
		{
			EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." curl_multi_select() failed",6);
		}
	}
	EchoMsgDebug("CurlMultipleWait() L#".(__LINE__),7);
	do 
	{
		EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." before curl_multi_info_read()",7);
		$event = curl_multi_info_read($MultiCurlHandle);
		EchoMsgDebug("CurlMultipleWait() L#".(__LINE__)." curl_multi_info_read = ".var_export($event,1),6);
		// result : 0 - ok, 28 - timeout
		if ($event)
		{
			if ($event['msg'] == 1)
			{
				$ch  = $event['handle'];
				$idx = $MultiCurlHandlesR[$ch];
				$cb  = $MultiCurlArray[$idx];
				$res = explode("\r\n\r\n",curl_multi_getcontent($ch));
				EchoMsgDebug("CurlMultipleWait L#".(__LINE__)." QueueThread({$cb['TID']},0,{$cb['function']},array_merge({$cb['params']},array('header'=>$res[0],'body'=>$res[1],'result'=>{$event['result']})))",6);
				QueueThread($cb['TID'],0,$cb['function'],array_merge($cb['params'],array("header"=>$res[0],"body"=>$res[1],"result"=>$event['result'])));
				curl_multi_remove_handle($MultiCurlHandle,$ch);
				unset($MultiCurlHandlesR[$ch]);
				unset($MultiCurlHandles[$idx]);
				unset($MultiCurlArray[$idx]);
				curl_close($ch);
			}
			else
			{
				EchoMsg("Unknown event : ".var_export($event,1));
			}
		}
	} while ($event);
	if (count($MultiCurlHandles))
	{
		QueueThread($MultiCurlThreadID,GetMicroTime()+50,"CurlMultipleWait",array());
		$MultiCurlActive = 1;
	}
	else
	{
		$MultiCurlActive = 0;
	}
}
function GetMicroTime()
{
	return floor(microtime(1)*100);
}
function DoThreading()
{
	global $Threads,$ThreadQueue;
	while (count($ThreadQueue))
	{
		reset($ThreadQueue);
		$time = key($ThreadQueue);
		$mt = GetMicroTime();
		EchoMsgDebug("DoThreading() time $mt, Task $time ({$ThreadQueue[$time]['Function']} {$ThreadQueue[$time]['ThreadID']})",5);
		if ($time <= $mt)
		{
			$Task = $ThreadQueue[$time];
			unset($ThreadQueue[$time]);
			ksort($ThreadQueue);
			$Function = $Task['Function'];
			$TID = $Task['ThreadID'];
			
			if (isset($Threads[$TID]))
			{
				foreach ($Threads[$TID]  as $TKey => $TVal)
				{
					$GLOBALS[$TKey] = &$Threads[$TID][$TKey];
				}
			}
			EchoMsgDebug("DoThreading() Call {$Function}($TID,".var_export($Task['Params'],1).")",5);
			$Function($TID,$Task['Params']);
		}
		else
		{
			$diff = $time - $mt;
			$diff2= $diff*10000;
			EchoMsgDebug("DoThreading() usleep({$diff2}) Difference = $diff",5);
			usleep($diff2);
		}
	}
}
function AddThread($name='',$func='',$p=array())
{
	global $ThreadsIndex,$Threads;
	$ThreadsIndex++;
	$Threads[$ThreadsIndex]=array(
		"ThreadID"	=> $ThreadsIndex,
		"ThreadName"	=> $name,
		"procId"	=> GetProcId($ThreadsIndex,$name),
	);
	if ($func)
	{
		//EchoMsg("QueueThread($ThreadsIndex,0,$func,".var_export($p,1).")");
		QueueThread($ThreadsIndex,0,$func,$p);
	}
	return $ThreadsIndex;
}
function RemoveThread($TID)
{
	global $Threads,$ThreadQueue;
	EchoMsgDebug("Removing Thread $TID {$Threads[$TID]['ThreadName']}",3);
	unset($Threads[$TID]);
	foreach($ThreadQueue as $time => $p)
	{
		if ($p['ThreadID'] == $TID)
		{
			unset($ThreadQueue[$time]);
		}
	}
}
function QueueThread($TID,$time,$function,$params)
{
	global $Threads,$ThreadQueue;
	while(isset($ThreadQueue[$time]))
	{
		$time++;
	}
	$ThreadQueue[$time]=array(
		"ThreadID"	=> $TID,
		"Function"	=> $function,
		"Params"	=> $params,
	);
	ksort($ThreadQueue);
}
function CallThread($function,$params,$time=0,$task='')
{
        global $ThreadsIndex,$Threads,$ThreadID;
        $TID = AddThread($function);
        $dbbt = debug_backtrace();
        $Threads[$TID]['callback']=$dbbt[1]['function'];
        $Threads[$TID]['calledf']=$function;
        $Threads[$TID]['callbackTID']=$ThreadID;
        $Threads[$TID]['callbacktask']=$task;
        foreach ($params as $k => $v)
        {
                $Threads[$TID][$k]=$v;
        }
        QueueThread($TID,0,$function,array("task"=>"init"));
}
function ReleaseThread($r)
{
        global $ThreadID,$callback,$callbackTID,$callbacktask,$calledf;
        if (!isset($r['task']))
                $r['task']=$callbacktask;
        if (!$r['task'])
                $r['task']=$calledf;
        QueueThread($callbackTID,0,$callback,$r);
        RemoveThread($ThreadID);
}
function ProxyLoad($TID,$p)
{
	global $Proxies,$ProxiesAvaiable,$ProxiesAlive,$ProxiesDead,$ProxiesRIndex,$ProxiesIndex,$args;
	$Proxies = array();
	if (isset($args['proxy']) AND is_file($args['proxy']))
	{
		$proxies = file($args['proxy']);
		foreach ($proxies as $i =>  $proxy)
		{
			$proxy = str_replace(array("\r\n","\r","\n"),array("","",""),$proxy);
			if (!isset($ProxiesRIndex[$proxy]))
			{
				$ProxiesIndex++;
				$Proxies[$ProxiesIndex]=array("proxy"=>$proxy,"proxytype"=>$args['proxy-type'],"conerrors"=>0,"pid"=>$ProxiesIndex);
				$ProxiesAvaiable[$ProxiesIndex]=$ProxiesIndex;
				$ProxiesRIndex[$proxy]=$ProxiesIndex;
				$ProxiesAlive++;
			}
		}
	}
	else
	{
		$Proxies[1]=array("proxy"=>$args['proxy'],"proxytype"=>$args['proxy-type'],"conerrors"=>0,"pid"=>1);
		$ProxiesAvaiable[1]=1;
		$ProxiesAlive++;
	}
	EchoMsg("ProxyLoad() {$args['proxy-type']} proxies from $args[proxy] (".count($Proxies).")");
}
function ProxyGet()
{
	global $Proxies,$ProxiesAvaiable,$Threads,$ThreadID,$proxy;
	if (count($ProxiesAvaiable))
	{
		$pid = reset($ProxiesAvaiable);
		unset($ProxiesAvaiable[$pid]);
		$Threads[$ThreadID]['proxy'] = $Proxies[$pid];
		$proxy = $Proxies[$pid];
		return $pid;
	}
	else
	{
		return 0;
	}
}
function ProxyRelease()
{
	global $Proxies,$ProxiesAvaiable,$Threads,$ThreadID,$proxy,$args;
	$pid = $proxy['pid'];
	unset($proxy);
	unset($Threads[$ThreadID]['proxy']);
	if (isset($Proxies[$pid]) and (!$args['proxy-max-errors'] OR $Proxies[$pid]['conerrors'] < $args['proxy-max-errors']))
	{
		$ProxiesAvaiable[$pid]=$pid;
	}
}
function ProxyReportOK()
{
	global $Proxies,$ProxiesAvaiable,$Threads,$ThreadID,$proxy;
	$pid = $proxy['pid'];
	EchoMsgDebug("ProxyReportOK() Proxy #$pid {$Proxies[$pid]['proxy']}",2);
	if (isset($Proxies[$pid]))
	{
		$Proxies[$pid]['conerrors']=0;
	}
}
function ProxyReportError()
{
	global $Proxies,$ProxiesAvaiable,$Threads,$ThreadID,$proxy,$args,$ProxiesAlive;
	$pid = $proxy['pid'];
	EchoMsgDebug("ProxyReportError() Proxy #$pid {$Proxies[$pid]['proxy']}",2);
	if (isset($Proxies[$pid]))
	{
		$Proxies[$pid]['conerrors']++;
		if (!$args['proxy-max-errors'] OR $Proxies[$pid]['conerrors'] < $args['proxy-max-errors'])
		{
			return 0;
		}
		else
		{
			$ProxiesAlive--;
			return 1;
		}
	}
	else
	{
		return 1;
	}
}
function ProxyReportBan()
{
	global $Proxies,$ProxiesAvaiable,$Threads,$ThreadID,$proxy,$args,$ProxiesAlive;
	$pid = $proxy['pid'];
	EchoMsgDebug("ProxyReportBan() Proxy #$pid {$Proxies[$pid]['proxy']}",2);
	if (isset($Proxies[$pid]))
	{
		$Proxies[$pid]['conerrors']=99999;
		if (!$args['proxy-max-errors'])
		{
			return 0;
		}
		else
		{
			$ProxiesAlive--;
			return 1;
		}
	}
	else
	{
		return 1;
	}
}
function ParseCookies($r,$c)
{
        preg_match_all("~Set-Cookie\: ([^\=]+)=([^\;]+)\;~",$r['header'],$m);
        if (is_array($m[1]))
        {
                foreach($m[1] as $i=>$j)
                {
                        $c[$j]=$m[2][$i];
                }
        }
        return $c;
}
function GetCookies($c)
{
        $ck = '';
        if (count($c))
        foreach($c as $i => $j)
        {
                $ck.= "{$i}={$j}; ";
        }
        return $ck;
}
?>